// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
using System;
using System.Collections.Generic;
using System.Threading;
using KnessetCore.Members;
using KnessetCore.Utils;
using log4net;

namespace KnessetCore.Updaters
{
	/// <summary>
	/// Updates the attendance sheet of knesset members.
	/// </summary>
	public class AttendingUpdater : IUpdater<Triplet<IDictionary<string, bool>, TimeSpan, DateTime>>
	{
		#region private variables
		private readonly ILog logger = LogManager.GetLogger("KnessetCore.Updaters");
		/// <summary>
		/// How much time to sleep before trying again.
		/// </summary>
		readonly TimeSpan sleepTime = new TimeSpan(0,0,10);
		#endregion

		#region IUpdater<IDictionary<string,bool>> Members
		/// <summary>
		/// Update a persistent object (like db or file) with the appropriate values.
		/// </summary>
		/// <param name="updateData">Update with this values.</param>
		/// <exception cref="UpdateException">Error while updating.</exception>
		public void Update(Triplet<IDictionary<string, bool>, TimeSpan, DateTime> updateData)
		{
			logger.Debug("Entered AttendingUpdater.Update");
			if (updateData == null) throw new ArgumentNullException("updateData");

			logger.Info("Updating hours table.");
			// Check that the time span is not 0. If it does then ignore it.
			if (updateData.Value2.TotalMilliseconds != 0)
			{
				foreach (KeyValuePair<string, bool> pair in updateData.Value1)
				{
					try
					{
						updateMember(updateData, pair);
					}
					catch (UpdateException)
					{
						// A simple error recovary, refresh the members list. Sometimes when the db goes
						// out of sync the (should happen) the members list is not syncronized.
						KnessetMembers.GetInstance().RefereshMembers();
						logger.Error("Error while trying to update, trying again.");
						// Before throwing an exception try again in another 10 seconds to update.
						Thread.Sleep(sleepTime);
						updateMember(updateData, pair);
					}
				}
			}
			logger.Debug("Exited AttendingUpdater.Update");
		}
		#endregion

		#region Helper methods
		/// <summary>
		/// Update a single member with the appropriate values.
		/// </summary>
		/// <param name="updateData">Updatw with this values.</param>
		/// <param name="pair">Holds the member information, key as name and value if member exists in 
		/// the house or not.</param>
		/// <exception cref="UpdateException">Error while trying to update.</exception>
		private void updateMember(Triplet<IDictionary<string, bool>, TimeSpan, DateTime> updateData, KeyValuePair<string, bool> pair)
		{
			logger.Debug("Entered AttendingUpdater.updateMember");
			try
			{
				// Check if member is considered a knesset member.
				if (KnessetMembers.GetInstance().IsExist(pair.Key))
				{
					// Now check if the knesset member is in the house.
					if (pair.Value)
					{
						logger.Info(string.Format("Member:{0} is in the house. Increasing its attendance sheet by {1}", pair.Key, updateData.Value2));
						// Yes it does, so add its count in the hours table.
						ITimeView member = KnessetMembers.GetInstance().GetByName(pair.Key);
						// Check if current member has more than 12 hours in his pocket.
						// More than 12 hours means something fishy.
						TimeSpan currentMembersHours = member.GetTotalHours(updateData.Value3, updateData.Value3);
						TimeSpan totalHours = currentMembersHours.Add(updateData.Value2);
						if (totalHours.CompareTo(Constants.SUSPECIOUS_OVER_TIME) <= 0)
						{
							member.AddWorkingHours(updateData.Value2, updateData.Value3);
						}
					}
				}
				else // Not a knesset member.
				{
					logger.Info("Member does not exist in members table: " + pair.Key);
				}
			}
			catch (Exception e)
			{
				logger.Error(string.Format("Error while trying to update. Error:  {0}\nStack: {1}", e.Message, e.StackTrace));
				throw new UpdateException(e.Message, e);
			}

			logger.Debug("Exited AttendingUpdater.updateMember");
		}
		#endregion
	}
}